﻿// Decompiled with JetBrains decompiler
// Type: MCM.LightInject.Scope
// Assembly: MCMv5, Version=5.10.2.0, Culture=neutral, PublicKeyToken=null
// MVID: 7AB326B9-B2B4-41C2-9177-68E944071101
// Assembly location: D:\steam\steamapps\workshop\content\261550\2859238197\bin\Win64_Shipping_Client\MCMv5.dll

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Threading;

#nullable disable
namespace MCM.LightInject
{
  [ExcludeFromCodeCoverage]
  internal class Scope : IServiceFactory, IDisposable
  {
    public bool IsDisposed;
    public Scope ParentScope;
    private readonly object lockObject = new object();
    private readonly IScopeManager scopeManager;
    private readonly ServiceContainer serviceFactory;
    private List<object> disposableObjects;
    private ImmutableMapTree<object> createdInstances = ImmutableMapTree<object>.Empty;

    public Scope(IScopeManager scopeManager, Scope parentScope)
    {
      this.scopeManager = scopeManager;
      this.serviceFactory = (ServiceContainer) scopeManager.ServiceFactory;
      this.ParentScope = parentScope;
    }

    public Scope(ServiceContainer serviceFactory) => this.serviceFactory = serviceFactory;

    public event EventHandler<EventArgs> Completed;

    public void TrackInstance(object disposable)
    {
      lock (this.lockObject)
      {
        if (this.disposableObjects == null)
          this.disposableObjects = new List<object>();
        this.disposableObjects.Add(disposable);
      }
    }

    public void Dispose()
    {
      if (this.disposableObjects != null && this.disposableObjects.Count > 0)
      {
        HashSet<IDisposable> disposableSet = new HashSet<IDisposable>((IEqualityComparer<IDisposable>) Scope.ReferenceEqualityComparer<IDisposable>.Default);
        for (int index = this.disposableObjects.Count - 1; index >= 0; --index)
        {
          if (!(this.disposableObjects[index] is IDisposable disposableObject))
            throw new InvalidOperationException(string.Format("The type {0} only implements `IAsyncDisposable` and can only be disposed in an asynchronous scope started with `BeginScopeAsync()`", (object) this.disposableObjects[index].GetType()));
          if (disposableSet.Add(disposableObject))
            disposableObject.Dispose();
        }
      }
      this.EndScope();
    }

    private void EndScope()
    {
      this.scopeManager?.EndScope(this);
      EventHandler<EventArgs> completed = this.Completed;
      if (completed != null)
        completed((object) this, new EventArgs());
      this.IsDisposed = true;
    }

    public Scope BeginScope() => this.serviceFactory.BeginScope();

    public object GetInstance(Type serviceType)
    {
      return this.serviceFactory.GetInstance(serviceType, this);
    }

    public object GetInstance(Type serviceType, string serviceName)
    {
      return this.serviceFactory.GetInstance(serviceType, this, serviceName);
    }

    public object GetInstance(Type serviceType, object[] arguments)
    {
      return this.serviceFactory.GetInstance(serviceType, arguments, this);
    }

    public object GetInstance(Type serviceType, string serviceName, object[] arguments)
    {
      return this.serviceFactory.GetInstance(serviceType, serviceName, arguments, this);
    }

    public object TryGetInstance(Type serviceType)
    {
      return this.serviceFactory.TryGetInstance(serviceType, this);
    }

    public object TryGetInstance(Type serviceType, string serviceName)
    {
      return this.serviceFactory.TryGetInstance(serviceType, serviceName, this);
    }

    public IEnumerable<object> GetAllInstances(Type serviceType)
    {
      return this.serviceFactory.GetAllInstances(serviceType, this);
    }

    public object Create(Type serviceType) => this.serviceFactory.Create(serviceType, this);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    internal object GetScopedInstance(
      GetInstanceDelegate getInstanceDelegate,
      object[] arguments,
      int instanceDelegateIndex)
    {
      object scopedInstance = this.createdInstances.Search<object>(instanceDelegateIndex);
      if (scopedInstance != null)
        return scopedInstance;
      lock (this.lockObject)
      {
        object disposable = this.createdInstances.Search<object>(instanceDelegateIndex);
        if (disposable == null)
        {
          disposable = getInstanceDelegate(arguments, this);
          if (disposable is IDisposable)
            this.TrackInstance(disposable);
          Interlocked.Exchange<ImmutableMapTree<object>>(ref this.createdInstances, this.createdInstances.Add<object>(instanceDelegateIndex, disposable));
        }
        return disposable;
      }
    }

    private class ReferenceEqualityComparer<T> : IEqualityComparer<T>
    {
      public static readonly Scope.ReferenceEqualityComparer<T> Default = new Scope.ReferenceEqualityComparer<T>();

      public bool Equals(T x, T y) => (object) x == (object) y;

      public int GetHashCode(T obj) => obj.GetHashCode();
    }
  }
}
